/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frmem.c                                            */
/*  NOTE      =                                                    */
/*  DATE      = 2014/10/10 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include "frcomm.h"
#include "frmem_chk.h"

#if FR_MEM_LEAK_CHECK
    //显示所有申请和释放的log 1:需要;0:不需要
    #define APM_SHOW_ALLMEM     0
    #define APM_NAME_MAX        128

    /* 节点 */
    typedef struct __st_listnode {
        struct __st_listnode  *next;
        void                  *data;
    } List_node;

    /* 头节点 */
    typedef struct __st_list {
        unsigned int   size;    /* 节点个数 */
        List_node    *head;
        List_node    *tail;
    } List_header;

    typedef struct __st_memunit
    {
        char str_file[APM_NAME_MAX];   //文件名
        char str_fun[56];          //函数名
        int i_line_no;             //行号
        void *p;
        //void (*pf_prt_data)(void* , char *, unsigned int);
        int i_size;
    } Mem_unit;

    /* 获取首节点(参数是Frlist_header) */
    #define LIST_FIRST_NODE(hdr)      ((hdr)->head)
    /* 获取下一个节点(参数是node) */
    #define LIST_NEXT_NODE(n)     ((n)?(n)->next:NULL)
    /* 获取节点数据 (参数是node) */
    #define LIST_GET_NODE_DATA(n) (n?(n)->data:NULL)

    //显示函数指针
    typedef void(*list_node_show)(List_node* p_st_node, char *p_outbuff, unsigned int ui_buff_size);
    //查询函数指针
    typedef int(*list_node_find)(List_node* p_st_node, const void *p_compare_data);

    pthread_rwlock_t  rwlock_mem_chk = PTHREAD_RWLOCK_INITIALIZER;
    List_header g_list_mem_chk_hdr={0, NULL, NULL};
    unsigned int g_ui_init_times=0;

    /*******************************************************************************
    *  FUNC     :  初始化头节点
    *  ARGS     :  *p_st_hdr (List_header)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    //static void __list_header_init(List_header *p_st_hdr)
    //{
    //    if(!p_st_hdr)
    //        return;
    //
    //    p_st_hdr->size = 0;
    //    p_st_hdr->head = p_st_hdr->tail = NULL;
    //    return;
    //}

    /*******************************************************************************
    *  FUNC     :  生成连表节点
    *  ARGS     :  p_data (连表中需要保存的数据)
    *  RTN      :  节点指针
    *  NOTE     :  p_data内存需要外部生成
    *******************************************************************************/
    static List_node* __list_node_create(void* p_data)
    {
        List_node *p_st_node = (List_node*) malloc(sizeof(List_node));
        if(p_st_node == NULL)
            return NULL;

        p_st_node->next = NULL;
        p_st_node->data = p_data;
        return p_st_node;
    }

    /*******************************************************************************
    *  FUNC     :  增加节点到尾部
    *  ARGS     :  *p_st_header (List_header)
                :  *p_st_node      (List_node)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    static void __list_push(List_header *p_st_header, List_node *p_st_node)
    {
        if(p_st_header == NULL || p_st_node == NULL)
            return;

        p_st_node->next = NULL;

        if (p_st_header->head) {
            p_st_header->tail->next = p_st_node;
            p_st_header->tail = p_st_node;

        } else
            p_st_header->head = p_st_header->tail = p_st_node;

        p_st_header->size++;
    }

    /*******************************************************************************
    *  FUNC     :  释放节点
    *  ARGS     :  *p_st_header    (List_header)
                :  *p_st_node_del  (List_node)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    static void __list_node_free(List_header *p_st_header, List_node *p_st_node_del)
    {
        List_node *p_st_node=NULL, *p_st_node_old=NULL;

        if(p_st_header == NULL || p_st_node_del == NULL )
            return;

        if (p_st_header->head == NULL)
            return;

        //头节点
        if(p_st_node_del == p_st_header->head)
        {
            p_st_header->head = p_st_header->head->next;

            if (p_st_header->head == NULL)
                p_st_header->tail = NULL;
            p_st_header->size--;

            if(p_st_node_del->data)
                free(p_st_node_del->data);
            free(p_st_node_del);

            return;
        }

        //中间节点
        p_st_node = p_st_header->head;
        p_st_node_old = p_st_node;
        p_st_node = p_st_node->next;

        while(p_st_node)
        {
            if(p_st_node == p_st_node_del)
            {
                p_st_node_old->next = p_st_node->next;
                if(p_st_node->next == NULL)
                    p_st_header->tail = p_st_node_old;

                if(p_st_node_del->data)
                    free(p_st_node_del->data);
                free(p_st_node_del);
                return;
            }

            p_st_node_old = p_st_node;
            p_st_node = p_st_node->next;
        }

        return;
    }

    /*******************************************************************************
    *  FUNC     :  查询节点
    *  ARGS     :  *p_st_header    (List_header)
                :  *p_data         (查询使用的数据)
                :  pf_find         (查询使用的函数指针)
    *  RTN      : 成功返回节点地址
    *  NOTE     :
    *******************************************************************************/
    static List_node *__list_find(List_header *p_st_header, const void *p_data, list_node_find pf_find)
    {
        List_node *p_st_node_tmp;
        int i_ret_fun;

        if(p_st_header == NULL || p_data == NULL || pf_find == NULL)
           return NULL;

        p_st_node_tmp = p_st_header->head;
        while(p_st_node_tmp)
        {
            i_ret_fun = pf_find(p_st_node_tmp, p_data);
            if(i_ret_fun ==0)
            {
                return p_st_node_tmp;
            }

            p_st_node_tmp = p_st_node_tmp->next;
        }

        return NULL;
    }

    /*******************************************************************************
    *  FUNC     :  显示连表内容
    *  ARGS     :  *p_st_header    (List_header)
                :  *p_outbuff      (出力数据空间)
                :  ui_buff_size    (出力数据空间大小)
                :  pf_show         (出力连表节点内容函数指针)
    *  RTN      : 成功返回节点地址
    *  NOTE     :
    *******************************************************************************/
    static void __list_show(List_header *p_st_header, char *p_outbuff, unsigned int ui_buff_size, list_node_show pf_show)
    {
        List_node *p_st_node_tmp;
        int i_used=0;

        if(p_st_header == NULL || pf_show == NULL)
            return;

        *p_outbuff = '\0';
        p_st_node_tmp = p_st_header->head;
        while(p_st_node_tmp && (ui_buff_size > i_used + 2) )
        {
            pf_show(p_st_node_tmp, p_outbuff+i_used, ui_buff_size - i_used);
            i_used += strlen(p_outbuff+i_used);

            p_st_node_tmp = p_st_node_tmp->next;
        }
        return;
    }

    /*******************************************************************************
    *  FUNC     :  删除链表所有节点
    *  ARGS     :  *pf_free (List_header)
                :  pf_free  (节点删除用函数指针)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    static void __list_remove_all(List_header *p_st_header)
    {
        List_node *p_st_node, *p_st_node_tmp;

        if(p_st_header == NULL)
            return;

        p_st_node = p_st_header->head;
        while(p_st_node)
        {
            p_st_node_tmp = p_st_node;
            p_st_node = p_st_node->next;

            free(p_st_node_tmp->data);
            free(p_st_node_tmp);
        }

        p_st_header->size = 0;
        p_st_header->head = p_st_header->tail = NULL;

        return;
    }

    /*******************************************************************************
    *  FUNC     :  缓存池初始化
    *  ARGS     :
    *  RTN      :  FR_SUCC (success)
    *           :  FR_FAIL (failed)
    *  NOTE     :
    *******************************************************************************/
    int __fr_memchk_init(const char *p_name)
    {
        __sync_fetch_and_add(&g_ui_init_times, 1);
        return FR_SUCC;
    }

    /*******************************************************************************
    *  FUNC     :  获取当前缓存池的信息
    *  ARGS     :
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_memchk_stat(char **pp_outbuff)
    {
        return;
    }

    /*******************************************************************************
    *  FUNC     :  内存分配函数(功能同malloc)
    *  ARGS     :  s_size   (内存空间大小)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    void *__fr_memchk_malloc(size_t s_size, const char *p_fname, const char *p_fun, int i_lineno)
    {
        Mem_unit *p_st_unit = NULL;
        List_node *p_st_node;

        if (s_size <= 0)
            return NULL;

        // 数据长度 > 100M报警告
        if(s_size > 104857600)
            printf("[%s(%s:%u)] size=%lu (input parameter err?)\n", p_fun,p_fname,i_lineno, s_size);

        p_st_unit = malloc(sizeof(Mem_unit));
        if(p_st_unit == NULL)
            return NULL;

        p_st_unit->p = malloc(s_size);
        if(p_st_unit->p == NULL)
        {
            free(p_st_unit);
            return NULL;
        }
        p_st_unit->i_size = s_size;

        p_st_unit->i_line_no = i_lineno;
        snprintf(p_st_unit->str_file, APM_NAME_MAX, "%s", p_fname);
        snprintf(p_st_unit->str_fun, sizeof(p_st_unit->str_fun), "%s", p_fun);

        pthread_rwlock_wrlock(&rwlock_mem_chk);
        p_st_node = __list_node_create(p_st_unit);
        __list_push(&g_list_mem_chk_hdr, p_st_node);
        pthread_rwlock_unlock(&rwlock_mem_chk);

    #if APM_SHOW_ALLMEM
        printf("malloc[%s:%d]size=%d paddr=%p\n", p_fname, i_lineno, s_size, p_st_unit->p);
    #endif
        return p_st_unit->p;
    }

    /*******************************************************************************
    *  FUNC     :  字符串拷贝，并重新生成内存空间(功能同strdup)
    *  ARGS     :  p_src    (拷贝字符串地址)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    char *__fr_memchk_strdup(const char *p_src, const char *p_fname, const char *p_fun, int i_lineno)
    {
        char *p_data = NULL;
        Mem_unit *p_st_unit = NULL;
        List_node *p_st_node;

        if (p_src == NULL)
            return NULL;

        p_st_unit = malloc(sizeof(Mem_unit));
        if(p_st_unit == NULL)
            return NULL;

        p_data = strdup(p_src);
        if(p_data == NULL)
        {
            free(p_st_unit);
            return NULL;
        }

        p_st_unit->p = p_data;
        p_st_unit->i_size = strlen(p_src)+1;

        p_st_unit->i_line_no = i_lineno;
        strncpy(p_st_unit->str_file, p_fname, APM_NAME_MAX);
        strncpy(p_st_unit->str_fun, p_fun,sizeof(p_st_unit->str_fun));

        pthread_rwlock_wrlock(&rwlock_mem_chk);
        p_st_node = __list_node_create(p_st_unit);
        __list_push(&g_list_mem_chk_hdr, p_st_node);
        pthread_rwlock_unlock(&rwlock_mem_chk);

    #if APM_SHOW_ALLMEM
        printf("strdup[%s:%d] paddr=%p\n", p_fname, i_lineno, p_st_unit->p);
    #endif
        return p_st_unit->p;
    }

    /*******************************************************************************
    *  FUNC     :  内存数据拷贝，并重新生成内存空间
    *  ARGS     :  p_src    (拷贝数据串地址)
                :  s_size   (拷贝数据大小)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    void *__fr_memchk_memdup(const char *p_src, size_t s_size, const char *p_fname, const char *p_fun, int i_lineno)
    {
        void *p_data;

        if (s_size <= 0)
            return NULL;

        p_data = __fr_memchk_malloc(s_size, p_fname, p_fun, i_lineno);
        if(!p_data)
            return NULL;

        memcpy(p_data, p_src, s_size);
        return p_data;
    }

    /*******************************************************************************
    *  FUNC     :  节点查询比较函数
    *  ARGS     :  *p_st_node      (List_node)
                :  *p_compare_data (比较用数据地址)
    *  RTN      : 成功返回节点地址
    *  NOTE     :
    *******************************************************************************/
    static int __list_node_find(List_node *p_st_node, const void *p_compare_data)
    {
        Mem_unit *p_st_unit = (Mem_unit *)(p_st_node->data);
        return !(p_compare_data == p_st_unit->p);
    }

    /*******************************************************************************
    *  FUNC     :  内存释放函数(功能同free)
    *  ARGS     :  p_free_data (释放地址)
                :  p_fname  (调用文件名)
                :  p_fun    (调用函数名)
                :  i_lineno (调用函数所在文件行号)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_memchk_free(void *p_free_data, const char *p_fname, const char *p_fun, int i_lineno)
    {
        List_node *p_st_node;
        int i_free = 0;

        if (p_free_data == NULL)
            return;

    #if APM_SHOW_ALLMEM
        printf("free[%s:%d] paddr=%p\n", p_fname, i_lineno, p_free_data);
    #endif

        pthread_rwlock_wrlock(&rwlock_mem_chk);
        p_st_node = __list_find(&g_list_mem_chk_hdr, p_free_data, __list_node_find);
        if(p_st_node)
        {
            i_free = 1;
            __list_node_free(&g_list_mem_chk_hdr, p_st_node);
        }
        pthread_rwlock_unlock(&rwlock_mem_chk);

        if(i_free)
            free(p_free_data);
        else
        {
            printf("already free?[%s:%d] paddr=%p\n", p_fname, i_lineno, p_free_data);
            //可能引起 double free
            free(p_free_data);
        }

        return;
    }

    /*******************************************************************************
    *  FUNC     :  节点内存出力函数
    *  ARGS     :  *p_st_node      (List_node)
                :  *p_outbuff      (出力数据地址)
                :  ui_buff_size    (出力数据空间大小)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    static void __list_node_show_mem_chk(List_node* p_st_node, char *p_outbuff, unsigned int ui_buff_size)
    {
        Mem_unit *p_st_unit = NULL;

        if(ui_buff_size <= 0 )
            return;

        p_st_unit = (Mem_unit *)(p_st_node->data);

        snprintf(p_outbuff, ui_buff_size, "[%s(%s:%d)] size=%d(%p)\n", p_st_unit->str_fun, p_st_unit->str_file, p_st_unit->i_line_no, p_st_unit->i_size, p_st_unit->p);
        return;
    }

    /*******************************************************************************
    *  FUNC     :  内存检查结束
    *  ARGS     :
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_memchk_destroy(void)
    {
        int i_size = 2048000;
        char *p_buff = NULL;

        __sync_fetch_and_sub(&g_ui_init_times, 1);
        if(g_ui_init_times > 0)
            return;

        p_buff = malloc(i_size);
        if(!p_buff)
            return;
        *p_buff = '\0';
        __list_show(&g_list_mem_chk_hdr, p_buff, i_size, __list_node_show_mem_chk);
        if(*p_buff == '\0')
            printf("no memory leak\n");
        else
            printf("memory leak\n%s\n", p_buff);
        free(p_buff);

        __list_remove_all(&g_list_mem_chk_hdr);

        return;
    }
#endif
